#include "SpcpClass.h"

SPCPClass::SPCPClass(ESPCPHeaderKind PHK, EISorNO PHI, PHSeekNumberType PHSN, PHDataNumberType PHDN, PHCheckSumType PHCS, PDataType PD)
    :m_ProtocolHeaderKind(PHK), m_ProtocolHeaderIsOffset(PHI), m_ProtocolHeaderSeekNumber(PHSN), m_ProtocolHeaderDataNumber(PHDN), m_ProtocolHeaderCheckSum(PHCS), m_ProtocolData(PD)
{

}

bool SPCPClass::PHKind(ESPCPHeaderKind* prorocolHeaderKind, ParameterKind kind)
{
    if (kind == ParameterKind::Set) {
        // 因为设置了枚举类型所以传入数据肯定无异常
        m_ProtocolHeaderKind = *prorocolHeaderKind;
    }
    else {
        *prorocolHeaderKind = static_cast<ESPCPHeaderKind>(m_ProtocolHeaderKind);
    }
    return true;
}

bool SPCPClass::PHIsOffset(EISorNO* isOffset, ParameterKind kind)
{
    if (kind == ParameterKind::Set) {
        // 因为设置了枚举类型所以传入数据肯定无异常
        m_ProtocolHeaderIsOffset = *isOffset;
    }
    else {
        *isOffset = static_cast<EISorNO>(m_ProtocolHeaderIsOffset);
    }
    return true;
}

bool SPCPClass::PHSeekNum(PHSeekNumberType* number, ParameterKind kind)
{
    if (kind == ParameterKind::Set) {
        m_ProtocolHeaderSeekNumber = *number;
    }
    else {
        *number = static_cast<PHSeekNumberType>(m_ProtocolHeaderSeekNumber);
    }
    return true;
}

bool SPCPClass::PHDataNumber(PHDataNumberType* data, ParameterKind kind)
{
    if (kind == ParameterKind::Set) {
        m_ProtocolHeaderDataNumber = *data;
    }
    else {
        *data = static_cast<PHDataNumberType>(m_ProtocolHeaderDataNumber);
    }
    return true;
}

bool SPCPClass::PHCheckSum(PHCheckSumType* checkSum, ParameterKind kind)
{
    if (kind == ParameterKind::Set) {
        m_ProtocolHeaderCheckSum = *checkSum;
    }
    else {
        *checkSum = static_cast<PHCheckSumType>(m_ProtocolHeaderCheckSum);
    }
    return true;
}

bool SPCPClass::Data(PDataType data, ParameterKind kind, int* dataLen)
{
    if (data == nullptr) return false;
    if (kind == ParameterKind::Set) {
        if (*dataLen == -1) return false;
        m_ProtocolData = new DataType[*dataLen];
        memcpy(m_ProtocolData, data, *dataLen);
        PHDataNumber((PHDataNumberType*)(dataLen), ParameterKind::Set);
        QByteArray empty;
        uint16_t checkNum = ComputCheckSum(empty);
        PHCheckSum(&checkNum, ParameterKind::Set);
    }
    else {
        data = static_cast<PDataType>(m_ProtocolData);
        if (m_ProtocolHeaderIsOffset == EISorNO::No) {
            // 当前数据包没有进行拆包处理，当前数据包实际有效数据长度就为该标志位的值
            *dataLen = (int)m_ProtocolHeaderDataNumber;
        }
        else if (m_ProtocolHeaderIsOffset == EISorNO::True) {
            // 当前数据包进行了拆包处理，当前数据包实际有效数据长度为当前标志位*4
            *dataLen = (int)m_ProtocolHeaderDataNumber * 4;
        }
        else {
            qDebug() << "偏移标志位未设置,无法进行有效数据长度计算";
            *dataLen = -1;
            return false;
        }
    }
    return true;
}

bool SPCPClass::CheckSPCPInfo(QString& errMsg)
{
    if (m_ProtocolHeaderKind == ESPCPHeaderKind::NullKind) {
        errMsg = "当前数据报类型暂未设置";
        return false;
    }
    else if (m_ProtocolHeaderIsOffset == EISorNO::null) {
        errMsg = "当前数据报是否偏移标志未被设置";
        return false;
    }
    else if (m_ProtocolData == nullptr) {
        errMsg = "当前数据包数据句柄为nullptr";
        return false;
    }
    else if (m_ProtocolHeaderCheckSum == 0x0000) {
        errMsg = "当前数据报未进行校验和计算";
        return false;
    }
    return true;
}

QByteArray SPCPClass::RClassToByteArray()
{
    QByteArray protocolHeader;
    // 保证protocolHeader一定能够装下当前数据报
    size_t dataLen = m_ProtocolHeaderIsOffset == EISorNO::True ? m_ProtocolHeaderDataNumber * 4 : m_ProtocolHeaderDataNumber;
    protocolHeader.resize(RProtocolHeadSize() + dataLen);
    // 所有数据按照小端机进行处理;
    QDataStream stream(&protocolHeader, QIODevice::ReadWrite);
    stream.setByteOrder(QDataStream::LittleEndian);

    // 此时封装协议类型(8bit)
    stream.device()->seek(PHKindLoc);       // 将指针偏移至pos字节处
    stream << m_ProtocolHeaderKind;

    // 封装是否偏移(8bit)
    stream.device()->seek(PHIsOffsetLoc);
    stream << m_ProtocolHeaderIsOffset;

    // 设置偏移量值(32bit)
    stream.device()->seek(PHSeekNumLoc);
    stream << m_ProtocolHeaderSeekNumber;

    // 设置有效数据长度(8bit)
    stream.device()->seek(PHDataNumLoc);
    stream << m_ProtocolHeaderDataNumber;

    // 设置滞留位(8bit)
    stream.device()->seek(PHSpaceLoc);
    stream << 0x00;

    // 设置校验和值(16bit)
    stream.device()->seek(PHCheckSumLoc);
    stream << m_ProtocolHeaderCheckSum;

    // 设置有效数据(n bit)
    stream.device()->seek(PDataLoc);
    for (int i = 0; i < (int)dataLen; ++i) {
        stream << (uint8_t) * (m_ProtocolData + i);
    }
    return protocolHeader;
}

uint16_t SPCPClass::ComputCheckSum(QByteArray& array)
{
    QByteArray Array;
    if (array.isEmpty()) Array = RClassToByteArray();
    else Array = array;

    QDataStream stream(&Array, QIODevice::ReadWrite);
    stream.setByteOrder(QDataStream::LittleEndian);
    uint32_t sum = 0;
    int len = Array.size();
    uint8_t* P8Array = new uint8_t[Array.size()];
    for (int i = 0; i < Array.size(); i++) {
        stream >> *(P8Array + i);
    }

     uint16_t* P16Array = (uint16_t*)P8Array;
     uint16_t* CheckNumber = (uint16_t*)(P8Array + PHCheckSumLoc);
    // 16位为单位进行相加(因为当前主机采用的是大端机所以这里是右侧为高位左侧为低位)
    while (len > 1) {
        sum += *P16Array++;
        len -= 2;
    }

    // 如果存在奇数位即（len % 2不为0）需要在后面手动加0x00
     if(len != 0) sum += *(uint8_t*)P16Array;

    // 保证校验和的值为0，所以在计算完校验和总值得减去校验和本身
    sum -= *CheckNumber;

    // 处理溢出的情况(保证校验和值小于等于16位) 将溢出的数值加在sum的最低位直至不溢出为止;因为当前项目32位足够所以当前判断逻辑既可
    while (sum >> 16)
    {
        sum = (sum & 0xffff) + (sum >> (sizeof(PHCheckSumType) / sizeof(char)) * 8);
    }
    uint16_t sum1 = (uint16_t)~sum;

    // PHCheckSum(&sum1, ParameterKind::Set);
    return sum1;
}

bool SPCPClass::DataToClass(QByteArray& Array, QString* errMsg)
{
    QDataStream stream(&Array, QIODevice::ReadOnly);
    stream.setByteOrder(QDataStream::LittleEndian);

    // 处理数据报类型标志位
    PHKindType tmpKind;
    stream >> tmpKind;
    if (tmpKind != ESPCPHeaderKind::Control && tmpKind != ESPCPHeaderKind::DataRevise && tmpKind != ESPCPHeaderKind::CameraPixelBMP && tmpKind != ESPCPHeaderKind::CameraImagePixel) {
        *errMsg = "传入数据报类型错误,请检查";
        return false;
    }
    m_ProtocolHeaderKind = tmpKind;

    // 处理数据报偏移标志位
    PHIsOffsetType tmpIsOffset;
    stream >> tmpIsOffset;
    if (tmpIsOffset != EISorNO::True && tmpIsOffset != EISorNO::No) {
        *errMsg = "传入数据报偏移标志位错误,请检查";
        return false;
    }
    m_ProtocolHeaderIsOffset = tmpIsOffset;

    // 处理数据报偏移值
    stream >> m_ProtocolHeaderSeekNumber;

    // 处理数据长度
    stream >> m_ProtocolHeaderDataNumber;

    uint8_t space;
    stream >> space;

    // 接收校验和值
    stream >> m_ProtocolHeaderCheckSum;
    uint16_t num = ComputCheckSum(Array);
    if (m_ProtocolHeaderCheckSum != num) {
        *errMsg = "当前数据报数据不完整, 请检查";
        return false;
    }

    // 接收数据内容;
    m_ProtocolData = new DataType[m_ProtocolHeaderDataNumber];
    for (int i = 0; i < m_ProtocolHeaderDataNumber; ++i)
    {
        stream >> *(m_ProtocolData + i);
    }

    return true;
}

